/*
 * Moseycode
 * Copyright (C) 2008  Tom Gibara
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package com.tomgibara.mosey.portal.render;

import com.tomgibara.mosey.portal.CodedPortal;
import com.tomgibara.mosey.portal.PortalSymbology;

/**
 * Used to render a portal into a generic graphical context.
 * An instance of this class may be used to repeatedly draw
 * a number of portals into the same graphical context.
 * 
 * Optionally, a customizer may be specified to control
 * the appearance of specific elements of the portals.
 * 
 * To render a portal directly, it is necessary to first wrap
 * it in a CodedPortal.
 * 
 * @author Tom Gibara
 *
 */

public class PortalPainter {

	private static final boolean PAINT_TARGETS_SOLID = false;
	
	private static final int DEFAULT_BACK_COLOR = 0xffffffff;
	private static final int DEFAULT_FORE_COLOR = 0xff000000;
	
	private static final int EDGE_COLOR = 0xffff0000;
	private static final int BITS_COLOR = 0xff0000ff;
	private static final int TARGET_COLOR = 0xff008000;
	private static final int DECAL_COLOR = 0xffffff00;
	
	private final GenericGraphics g;
	
	private int backgroundColor = DEFAULT_BACK_COLOR;
	private int foregroundColor = DEFAULT_FORE_COLOR;
	private PortalPainterCustomizer customizer = null;
	private boolean debug;
	
	public PortalPainter(GenericGraphics g) {
		this.g = g;
	}

	public GenericGraphics getGraphics() {
		return g;
	}
	
	// accessors
	
	public void setForegroundColor(int foregroundColor) {
		if (foregroundColor == 0) foregroundColor = DEFAULT_FORE_COLOR;
		this.foregroundColor = foregroundColor;
	}
	
	public int getForegroundColor() {
		return foregroundColor;
	}
	
	public void setBackgroundColor(int backgroundColor) {
		if (backgroundColor == 0) backgroundColor = DEFAULT_BACK_COLOR;
		this.backgroundColor = backgroundColor;
	}
	
	public int getBackgroundColor() {
		return backgroundColor;
	}

	public void setCustomizer(PortalPainterCustomizer customizer) {
		this.customizer = customizer;
	}
	
	public PortalPainterCustomizer getCustomizer() {
		return customizer;
	}
	
	public void setDebug(boolean debug) {
		this.debug = debug;
	}
	
	public boolean isDebug() {
		return debug;
	}
	
	// methods
	
	public void paint(CodedPortal cp) {
		final boolean debug = this.debug;
		final float radius = PortalSymbology.BIT_RADIUS;
		final float diameter = radius * 2;
		final float cornerRadius = PortalSymbology.CORNER_RADIUS;
		int foregroundColor = customizer == null ? 0 : customizer.getForegroundColor();
		if (foregroundColor == 0) foregroundColor = this.foregroundColor;
		int backgroundColor = customizer == null ? 0 : customizer.getBackgroundColor();
		if (backgroundColor == 0) backgroundColor = this.backgroundColor;
		
		if (debug) {
			g.setFractionalMetrics(true);
			//g.setStroke(0.005f);
			g.scaleFont(0.020f);
		}

		//paint quiet area
		g.setColor(backgroundColor);
		boolean quietSet = false;
		if (customizer != null) quietSet = customizer.setQuietArea(g); 
		if (!quietSet) g.setRoundedRect(0,0,1,1,cornerRadius,cornerRadius);
		g.fill();
		
		if (customizer != null) customizer.renderDecoration(g);
		
		//quit if there's no data
		final int[] codewords = cp.getCodewords();
		if (codewords == null) return;

		//paint bits
		if (debug) {
			g.setColor(BITS_COLOR);
		} else {
			g.setColor(foregroundColor);
		}
		for (int word = 0; word < PortalSymbology.CODEWORDS; word++) {
			float[] coords = PortalSymbology.getBitCoords(word);
			int cw = codewords[word];
			for (int bit = 0; bit < PortalSymbology.CODEWORD_LENGTH; bit++) {
				final boolean on = debug || ((cw >> ((PortalSymbology.CODEWORD_LENGTH-1)-bit)) & 1) == 1;
				float x = coords[bit*2    ];
				float y = coords[bit*2 + 1];
				if ((debug || customizer == null || !customizer.renderDataBit(g, x, y, on)) && on) {
					g.setEllipse(x-radius, y-radius, diameter, diameter);
					g.fill();
					if (debug) {
						g.setString("" + (char)(97 + word) + bit);
						g.setColor(DEFAULT_BACK_COLOR);
						float w = g.getWidth();
						g.setPoint(x-w * 0.5f, y+diameter* 0.25f);
						g.setColor(BITS_COLOR);
					}
				}
			}
		}
		
		//paint edge bits
		if (debug) g.setColor(EDGE_COLOR);
		float[] edgeBitCoords = PortalSymbology.getEdgeBitCoords();
		int edgeBits = PortalSymbology.getEdgeBits();
		for (int e = 0; e < PortalSymbology.EDGE_BITS; e++) {
			boolean on = (edgeBits >> ((PortalSymbology.EDGE_BITS-1)-e) & 1) == 1;
			float x = edgeBitCoords[e*2    ];
			float y = edgeBitCoords[e*2 + 1];
			if (debug || ((customizer == null || !customizer.renderEdgeBit(g, x, y, on)) && on)) {
				g.setEllipse(x-radius, y-radius, diameter, diameter);
				if (on) g.fill();
				if (debug) {
					float r = radius * 0.8f;
					g.setEllipse(x-r, y-r, r*2f, r*2f);
					g.fill();
				}
			}
		}
		
		//paint targets
		if (PAINT_TARGETS_SOLID) {
			int targetColor = customizer == null ? 0 : customizer.getTargetColor();
			if (targetColor == 0) targetColor = foregroundColor;
			float[] targetCoords = PortalSymbology.getTargetCoords();
			float[] targetRadii = PortalSymbology.getTargetRadii();
			for (int i = 0; i < PortalSymbology.TARGETS; i++) {
				float x = targetCoords[i*2];
				float y = targetCoords[i*2+1];
				for (int j = 0; j < targetRadii.length; j++) {
					int color;
					if (debug) {
						color = (j & 1)==0 ? TARGET_COLOR : backgroundColor;
					} else {
						color = (j & 1)==0 ? targetColor : backgroundColor;
					}
					float r = targetRadii[j];
					float d = 2f * r;
					g.setColor(color);
					g.setEllipse(x-r, y-r, d, d);
					g.fill();
				}
			}
		} else {
			int targetColor = customizer == null ? 0 : customizer.getTargetColor();
			if (targetColor == 0) targetColor = foregroundColor;
			float[] targetCoords = PortalSymbology.getTargetCoords();
			float[] targetRadii = PortalSymbology.getTargetRadii();
			g.setColor(targetColor);
			for (int i = 0; i < PortalSymbology.TARGETS; i++) {
				float x = targetCoords[i*2];
				float y = targetCoords[i*2+1];
				for (int j = 0; j < targetRadii.length; j+=2) {
					float r0 = targetRadii[j];
					float r1 = targetRadii[j+1];
					float d = r0 + r1;
					float r = d / 2f;
					g.setStroke(r0 - r1);
					g.setEllipse(x-r, y-r, d, d);
					g.stroke();
				}
			}
		}
		
		if (debug) {
			g.setColor(DECAL_COLOR);
			float[] coords = PortalSymbology.getDecalCoords();
			g.setRectangle(coords[0], coords[1], coords[2] - coords[0], coords[3] - coords[1]);
			g.fill();
		}

	}
	
}
